#include "TcpServer.h"
#include <netinet/tcp.h>
#include <fcntl.h>

namespace BORNS {

TcpServer::TcpServer(uint16_t port, TcpProtocol::Type p):
    acceptThread_(nullptr),
    running_(false),
    proType_(p)
{
    //第一步：创建套接字
    sockListen_ = socket(AF_INET, SOCK_STREAM, 0);
    //    fcntl(sockListen_, F_SETFL, O_NONBLOCK);//设置非阻塞
    if(sockListen_ == -1)
    {
        perror("socket");
        return;
    }


    //设置套接字属性，以允许地址复用
    int val = 1;
    setsockopt(sockListen_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
    //第二步：绑定地址
    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;//指定地址家族
    myaddr.sin_addr.s_addr = INADDR_ANY;//指定使用本机任意地址
    //myaddr.sin_addr.s_addr = inet_addr("114.115.246.184");//指定IP地址
    myaddr.sin_port = htons(port);//指定端口号

    if(-1 == bind(sockListen_, (struct sockaddr*)&myaddr, sizeof(myaddr)))
    {
        perror("bind");
        return;
    }

    //第三步：监听
    if(-1 == listen(sockListen_, 5))//第二个参数表示连接等待队列的长度
    {
        perror("listen");
        return;
    }
    printf("server running:port[%d]\n", port);
    running_.store(true);
    // 创建接收客户端链接请求的线程
    acceptThread_ = new std::thread(&TcpServer::AcceptThread, this);
}

TcpServer::~TcpServer()
{
    running_.store(false);
    if(acceptThread_){
        if(acceptThread_->joinable()){
            acceptThread_->join();
        }
    }
}

bool TcpServer::Read(int sock, uint8_t *buf, const size_t buf_len, size_t &read)
{
    read = 0;
    ssize_t res = ::recv(sock, buf, buf_len, 0);

    if (res == 0)
    {
        return false;
    }
    else if (res < 0)
        return false;

    read = static_cast<size_t>(res);
    return true;
}

bool TcpServer::Read(const TcpProtocol::Device &dev, uint8_t *buf, const size_t buf_len, size_t &read)
{
    auto iter = clients_.find(dev);
    if(iter == clients_.end())// 没有该设备
        return false;
    return this->Read(iter->second, buf, buf_len, read);
}

bool TcpServer::Write(int sock, const uint8_t *buf, const size_t buf_len, size_t &written)
{
    written = 0;
    size_t remaining = buf_len;

    // handle partial sends
    while (written < buf_len)
    {
        ssize_t sent = ::send(sock, buf + written, remaining, 0);

        if (sent <= 0)
        {
            return false;
        }

        written += sent;
        remaining -= sent;
    }
    return true;
}

bool TcpServer::Write(int sock, const uint8_t *buf, const size_t buf_len)
{
    size_t written = 0;
    if(proType_ == TcpProtocol::Type::HeaderData){
        uint8_t realBuf[TcpProtocol::maxWriteDataSize];
        size_t size = 0;
        TcpProtocol::PackBuffer(buf, buf_len, realBuf, size);
        if(this->Write(sock, realBuf, size, written)){
            return true;
        }
    }else{
        if(this->Write(sock, buf, buf_len, written)){
            return true;
        }
    }
    return false;
}

bool TcpServer::Write(int sock, const std::vector<uint8_t> &buf)
{
    return this->Write(sock, buf.data(), buf.size());
}

bool TcpServer::Write(int sock, void *buf, size_t buf_len)
{
    uint8_t realBuf[TcpProtocol::maxWriteDataSize];
    memcpy(realBuf, buf, buf_len);
    return this->Write(sock, realBuf, buf_len);
}

bool TcpServer::Write(const TcpProtocol::Device &dev, const uint8_t *buf, const size_t buf_len)
{
    auto iter = clients_.find(dev);
    if(iter == clients_.end())// 没有该设备
        return false;
    return this->Write(iter->second, buf, buf_len);
}

bool TcpServer::Write(const TcpProtocol::Device &dev, const std::vector<uint8_t> &buf)
{
    return this->Write(dev, buf.data(), buf.size());
}

bool TcpServer::Write(const TcpProtocol::Device &dev, void *buf, size_t buf_len)
{
    uint8_t realBuf[TcpProtocol::maxWriteDataSize];
    memcpy(realBuf, buf, buf_len);
    return this->Write(dev, realBuf, buf_len);
}

bool TcpServer::WriteAll(const uint8_t *buf, size_t buf_len)
{
    if(clients_.empty()){
        printf("client empty\n");
        return false;
    }
    size_t  written = 0;
    if(proType_ == TcpProtocol::Type::HeaderData){
        uint8_t realBuf[TcpProtocol::maxWriteDataSize];
        size_t size = 0;
        TcpProtocol::PackBuffer(buf, buf_len, realBuf, size);
        //        printf("size: %lu\n", size);
        //        printf("%02X %02X %02X %02X %02X %02X %02X %02X "
        //               "%02X %02X %02X %02X %02X %02X %02X %02X\n",
        //               realBuf[0], realBuf[1], realBuf[2], realBuf[3],
        //               realBuf[4], realBuf[5], realBuf[6], realBuf[7],
        //               realBuf[8], realBuf[9], realBuf[10], realBuf[11],
        //               realBuf[12], realBuf[13], realBuf[14], realBuf[15]);
        for(auto ele:clients_){
            if(!this->Write(ele.second, realBuf, size, written))
                return false;
        }
    }else{
        for(auto ele:clients_){
            if(!this->Write(ele.second, buf, buf_len, written)){
                printf("Write ele.second failed\n");
                return false;
            }
        }
    }
    return true;
}

bool TcpServer::WriteAll(const std::vector<uint8_t> &buf)
{
    return this->WriteAll(buf.data(), buf.size());
}

bool TcpServer::WriteAll(const void *buf, size_t buf_len)
{
    uint8_t realBuf[TcpProtocol::maxWriteDataSize];
    memcpy(realBuf, buf, buf_len);
    return this->WriteAll(realBuf, buf_len);
}

void TcpServer::SetRcvFunc(std::function<void (std::vector<uint8_t> &, const TcpProtocol::Device &)> f)
{
    parseFunc_ = f;
}

void TcpServer::AcceptThread()
{
    while(running_.load())
    {
        //第四步：接收客户端连接请求
        struct sockaddr_in client_addr;
        socklen_t addr_len = sizeof(client_addr);
        /*
               accept 函数参数解读：
               第一个参数为监听套接字描述符；
               第二个参数为地址结构体(struct sockaddr_in)指针，用于接收客户端的地址信息，如果对客户端地址不感兴趣，就传 NULL；
               第三个参数为地址结构体长度，用于接收客户端地址长度，如果对客户端地址不感兴趣，就传 NULL。
               如果成功，accept 函数的返回值为这条连接对应的套接字（通常称为连接套接字），后面使用这个套接字进行收发数据。如果失败，其返回值为
     -1。
               调用 accept 函数时，如果没有任何客户端连接请求到来，该函数会阻塞调用线程直到成功接收到一个客户端连接请求或出现错误才返回。
               */
        int sock_conn = accept(sockListen_, (struct sockaddr*)&client_addr, &addr_len);

        if(sock_conn == -1)
        {
            perror("accept");
        }
        char ip[16];
        strcpy(ip, inet_ntoa(client_addr.sin_addr));
        int port = 0;
        port = ntohs(client_addr.sin_port);
        int flag = 1;
        fcntl(sock_conn, F_SETFL, O_NONBLOCK);//设置非阻塞
        setsockopt(sock_conn, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(int));
        setsockopt(sock_conn, SOL_TCP, TCP_KEEPIDLE, &flag, sizeof(int));
        setsockopt(sock_conn, SOL_TCP, TCP_KEEPINTVL, &flag, sizeof(int));
        setsockopt(sock_conn, SOL_TCP, TCP_KEEPCNT, &flag, sizeof(int));
        // 一个链接对应一个接收数据线程
        std::thread(&TcpServer::RcvThread, this, sock_conn).detach();
        printf("\n客户端(%s:%d)已连接！\n", ip, port);
    }
    //第七步：关闭监听套接字
    close(sockListen_);
}

void TcpServer::RcvThread(int sockId)
{
    struct timeval timeout;// select超时时间
    fd_set readfds;
    std::vector<uint8_t> readBuffer;// 读取数据的总缓存
    uint8_t buf[TcpProtocol::maxReadDataSize];// 每次读取的数据缓存
    bool first = true;
    while(running_.load()){
        FD_ZERO(&readfds);
        FD_SET(sockId, &readfds);
        timeout.tv_sec = 0;  // do this each loop as selects modifies timeout
        timeout.tv_usec = 500000;  // timeout of 0.5 sec
        int selectRet = select(sockId + 1, &readfds, nullptr, nullptr, &timeout);
        // 文件描述符对应的IO有数据
        if(selectRet == 1){
            // 读取数据到buffer
            memset(buf, 0, TcpProtocol::maxReadDataSize);
            size_t bytesRead{0};
            // 读取成功
            if(this->Read(sockId, buf, TcpProtocol::maxReadDataSize, bytesRead)){
                readBuffer.insert(readBuffer.cend(), buf, buf + bytesRead);
                if(proType_ == TcpProtocol::Type::HeaderData){
                    // 拆包并解析数据
                    Unpack(sockId, readBuffer, first);
                }else if(parseFunc_){
                    if(first){
                        first = false;
                        clients_[TcpProtocol::Device::Unknown] = sockId;
                    }
                    parseFunc_(readBuffer, TcpProtocol::Device::Unknown);
                }
            }else{// 读取错误

            }
        }
    }
}

void TcpServer::Unpack(int sockId, std::vector<uint8_t> &bytes, bool &isFirst)
{
    // 该解析数据线程所属的设备类型
    static thread_local TcpProtocol::Device theDev{TcpProtocol::Device::Unknown};
    while(bytes.size() >= TcpProtocol::PACKAGE_SIZE){
        // 获取UR数据包的包头信息即包的大小
        size_t packetSize = 0;
        memcpy(&packetSize, bytes.data(), TcpProtocol::PACKAGE_SIZE);
        if (bytes.size() >= packetSize) {
            // 删除包头信息
            bytes.erase(bytes.cbegin(), bytes.cbegin() + TcpProtocol::PACKAGE_SIZE);
            // 首次解析设备种类的信息
            if(isFirst){
                isFirst = false;
                memcpy(&theDev, bytes.data(), sizeof(theDev));
                clients_[theDev] = sockId;
                // test code
                switch (theDev) {
                case TcpProtocol::Device::MainController:
                    printf("检测到设备 MainController 连接成功\n");
                    break;
                case TcpProtocol::Device::LowerController:
                    printf("检测到设备 LowerController 连接成功\n");
                    break;
                case TcpProtocol::Device::Robot:
                    printf("检测到设备 Robot 连接成功\n");
                    break;
                case TcpProtocol::Device::Unknown:
                    printf("检测到设备 Unknown 连接成功\n");
                    break;
                }
                // end
            }
            // 解析数据包
            else if(parseFunc_){
                parseFunc_(bytes, theDev);
            }
            // 删除已解析的数据包
            bytes.erase(bytes.cbegin(), bytes.cbegin() + packetSize - TcpProtocol::PACKAGE_SIZE);
        } else {
            break;
        }
    }
}

}// namespace BORNS
